# -*- coding: utf-8 -*-
# @Time    : 2020/12/14 12:57
# @Author  : Guo_Zhanyu
# @File    : BoardBasics.py
import sys
import MainWindowNew
import Dialog
from PyQt5.QtCore import Qt, QCoreApplication
from PyQt5.QtWidgets import QMainWindow, QDialog, QApplication, QWidget
from PyQt5.QtGui import QPalette, QColor, QPainter, QPen, QBrush
import socket
from functools import partial


# 棋盘窗口绘制
"""Globals"""
SIZE = 30  # 棋盘每个点的距离
LINES = 19  # 棋盘的行/列数
OUTER_WIDTH = 20  # 棋盘外宽度
INSIDE_WIDTH = 4  # 边框与棋盘最外的小间隔
BORDER_WIDTH = 4  # 边框宽度
LINE_WIDTH = 1
BORDER_LENGTH = SIZE*(LINES - 1) + INSIDE_WIDTH*2 + BORDER_WIDTH  # 边框线长度
START_X = START_Y = OUTER_WIDTH + int(BORDER_WIDTH/2) + INSIDE_WIDTH  # 边框左上角坐标
SCREEN_HEIGHT = SIZE*(LINES - 1) + OUTER_WIDTH*2 + BORDER_WIDTH + INSIDE_WIDTH*2  # 屏幕高度
SCREEN_WIDTH = SCREEN_HEIGHT + 200  # 游戏屏幕的宽

# 棋子绘制
STONE_R = SIZE//2 - 3  # 棋子半径
STONE_R2 = SIZE//2 + 3

# 颜色
BOARD_COLOR = QColor(0xFA, 0xBE, 0x6E)  # 背景颜色
BLACK_COLOR = QColor(0, 0, 0)  # 黑色
WHITE_COLOR = QColor(255, 255, 255)  # 白色
RED_COLOR = QColor(200, 30, 30)  # 红色
BLUE_COLOR = QColor(30, 30, 200)  # 蓝色
BLACK_STONE_COLOR = QColor(45, 45, 45)  # 黑子颜色
WHITE_STONE_COLOR = QColor(219, 219, 219)  # 白子颜色
YELLOW_STONE_COLOR = QColor(255, 215, 0)  # 黄子颜色

# 偏移量，用以遍历棋盘
offset = [(1, 0), (0, 1), (1, 1), (1, -1)]


class Chessman:
    """
        class Chessman
        @ name: Chessman's name ('black', 'white'[, 'yellow']).
        @ value: Chessman's value (1, 2[, 3]).
        @ color: Chessman's color (BLACK_STONE_COLOR, WHITE_STONE_COLOR[, YELLOW_STONE_COLOR]).
        @ usage: Define a Chessman's Properties.
    """
    def __init__(self, name, value, color):
        self.name = name
        self.value = value
        self.color = color
        pass
    pass


BLACK_CHESSMAN = Chessman('black', 1, BLACK_STONE_COLOR)
WHITE_CHESSMAN = Chessman('white', 2, WHITE_STONE_COLOR)


class Point:
    """
        class Point
        @ x: axis x value.
        @ y: axis y value.
        @ usage: Point(x, y) means a point on the board's frame.
        @ advanced: Y is from Up to Down, X is from Left to Right.
    """
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y
        pass
    pass


class Board:
    """
        class Board
        @ lines: board's lines.
        @ usage: basic functions
    """
    def __init__(self, lines):
        self._lines = lines
        self._board = [[0] * self._lines for _ in range(self._lines)]  # 矩阵[LINESxLINES]
        self.cur_runner = BLACK_CHESSMAN
        self.winner = None
        self.last = None
        self.history = []
        pass

    def getBoard(self):
        return self._board

    def setBoard(self, new_board):
        self._board = new_board
        pass

    def canDrop(self, point):
        return self._board[point.y][point.x] == 0

    def drop(self, chessman, point):
        self._board[point.y][point.x] = chessman.value
        self.last = point
        self.history.append(point)

        # 判断胜利
        if self.win(point):
            print(f'{chessman.name} win, (Press SPACE to continue...)')
            return chessman
        pass

    def win(self, point):
        cur_value = self._board[point.y][point.x]
        for item in offset:
            if self.getCountOnDirection(point, cur_value, item[0], item[1]) >= 5:
                return True
            pass
        pass

    def getCountOnDirection(self, point, value, x_offset, y_offset):
        count = 1
        for step in range(1, 5):
            x = point.x + step*x_offset
            y = point.y + step*y_offset
            if 0 <= x < self._lines and 0 <= y < self._lines and self._board[y][x] == value:
                count += 1
            else:
                break
                pass
            pass

        for step in range(1, 5):
            x = point.x - step*x_offset
            y = point.y - step*y_offset
            if 0 <= x < self._lines and 0 <= y < self._lines and self._board[y][x] == value:
                count += 1
            else:
                break
                pass
            pass

        return count

    def getNextRunner(self, cur_runner):
        if cur_runner == BLACK_CHESSMAN:
            return WHITE_CHESSMAN
        else:
            return BLACK_CHESSMAN
        pass

    pass


class BoardGUI(QMainWindow):
    """
        class BoardGUI --> QMainWindow
        @ lines: board's lines.
        @ usage: Generate and Update a GUI, Connected with Server and Update timely.
    """
    def __init__(self, lines):
        super(BoardGUI, self).__init__()
        self.MainWindowUI = MainWindowNew.Ui_MainWindow()
        self.MainWindowUI.setupUi(self)

        self.Dialog = QDialog()
        self.DialogUI = Dialog.Ui_Dialog()
        self.DialogUI.setupUi(self.Dialog)

        palette1 = QPalette()
        palette1.setColor(self.backgroundRole(), BOARD_COLOR)
        self.setPalette(palette1)

        self._lines = lines
        self.board = Board(lines)

        # 服务器地址与端口号（默认值），后续可以输入
        self.url = 'http://127.0.0.1:5000/'

        self.MainWindowUI.lineEdit_2.setText(self.getLocalIP())
        self.MainWindowUI.pushButton.clicked.connect(partial(self.buttonFunc, self.MainWindowUI))
        self.MainWindowUI.pushButton_4.clicked.connect(self.regret)

        self.DialogUI.buttonBox.accepted.connect(self.rerun)
        self.DialogUI.buttonBox.rejected.connect(self.notrerun)
        pass

    def regret(self):
        if self.board.history:
            board = self.board.getBoard()
            board[self.board.last.y][self.board.last.x] = 0
            self.board.setBoard(board)
            self.board.cur_runner = self.board.getNextRunner(self.board.cur_runner)

            self.board.history.pop(-1)
            if self.board.history:
                self.board.last = self.board.history[-1]
            else:
                self.board.last = None
            self.update()
        else:
            print('Can regret')
            pass

        pass

    def rerun(self):
        self.board = Board(self._lines)
        pass

    def notrerun(self):
        self.board.winner = None
        pass

    def paintEvent(self, event):
        qp = QPainter(self)
        qp.begin(self)
        # 抗锯齿
        qp.setRenderHint(QPainter.Antialiasing)
        self.drawBoard(qp)  # 画棋盘
        self.drawStones(qp)  # 画棋子

        if self.board.winner is not None:
            self.DialogUI.label.setText(f'{self.board.winner.name} 胜利，游戏结束，是否重新开始？')
            self.Dialog.show()
            pass

        qp.end()
        pass

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            if self.board.winner is None:
                mouse_pos = [event.pos().x(), event.pos().y()]
                # print(mouse_pos)
                click_point = self.getClickPoint(mouse_pos)
                if click_point is not None:
                    if self.board.canDrop(click_point):
                        self.board.winner = self.board.drop(self.board.cur_runner, click_point)
                        self.board.cur_runner = self.board.getNextRunner(self.board.cur_runner)
                        pass
                    pass
                else:
                    print('超过棋盘范围...')
                    pass
                self.update()
                pass
            pass
        elif event.button() == Qt.RightButton:
            if self.board.winner is not None:
                self.board = Board(self._lines)
                self.update()
                pass
            pass
        pass

    def getClickPoint(self, click_pos):
        pos_x = click_pos[0] - START_X
        pos_y = click_pos[1] - START_Y
        if pos_x < -INSIDE_WIDTH or pos_y < -INSIDE_WIDTH:
            return None
        x = pos_x // SIZE
        y = pos_y // SIZE
        if pos_x % SIZE > SIZE / 2:
            x += 1
            pass
        if pos_y % SIZE > SIZE / 2:
            y += 1
            pass
        if x >= LINES or y >= LINES:
            return None

        return Point(x, y)

    def drawStones(self, qp):
        color = BLACK_STONE_COLOR if self.board.cur_runner == BLACK_CHESSMAN else WHITE_STONE_COLOR
        pen = QPen(color, LINE_WIDTH, Qt.SolidLine)
        brush = QBrush(color)
        qp.setPen(pen)
        qp.setBrush(brush)
        qp.drawEllipse(650, 30, STONE_R2*2, STONE_R2*2)
        if self.board.last is not None:
            pen.setColor(RED_COLOR)
            brush = QBrush(Qt.NoBrush)
            qp.setBrush(brush)
            qp.setPen(pen)
            qp.drawRect(START_X + SIZE * self.board.last.x - STONE_R, START_Y + SIZE * self.board.last.y - STONE_R,
                        STONE_R * 2, STONE_R * 2)
            pass

        for i, row in enumerate(self.board.getBoard()):
            for j, cell in enumerate(row):
                if cell == BLACK_CHESSMAN.value:
                    self.drawStone(qp, Point(j, i), BLACK_CHESSMAN.color)
                elif cell == WHITE_CHESSMAN.value:
                    self.drawStone(qp, Point(j, i), WHITE_CHESSMAN.color)
                    pass
                pass
            pass
        pass

    def drawStone(self, qp, point, stone_color):
        pen = QPen(stone_color, LINE_WIDTH, Qt.SolidLine)
        qp.setPen(pen)
        qp.setBrush(stone_color)
        qp.drawEllipse(START_X + SIZE * point.x - STONE_R, START_Y + SIZE * point.y - STONE_R,
                       STONE_R * 2, STONE_R * 2)
        pass

    def drawBoard(self, qp):
        pen = QPen(BLACK_COLOR, BORDER_WIDTH, Qt.SolidLine)
        qp.setPen(pen)
        qp.drawRect(OUTER_WIDTH, OUTER_WIDTH, BORDER_LENGTH, BORDER_LENGTH)
        self.drawLines(qp)
        self.drawBoardPoints(qp)
        pass

    def drawLines(self, qp):
        pen = QPen(BLACK_COLOR, LINE_WIDTH, Qt.SolidLine)
        qp.setPen(pen)
        for i in range(LINES):
            qp.drawLine(START_X, START_Y + SIZE*i, START_X + SIZE*(LINES - 1), START_Y + SIZE*i)
            pass

        for j in range(LINES):
            qp.drawLine(START_X + SIZE*j, START_Y, START_X + SIZE*j, START_Y + SIZE*(LINES - 1))
            pass
        pass

    def drawBoardPoints(self, qp):
        qp.setBrush(BLACK_COLOR)
        for i in (3, 9, 15):
            for j in (3, 9, 15):
                if i == j == 9:
                    r = 5
                else:
                    r = 3
                    pass
                qp.drawEllipse(START_X + SIZE*i - r, START_Y + SIZE*j - r, r*2, r*2)
                pass
            pass
        pass

    def buttonFunc(self, ui):
        dataIn = ui.lineEdit.text()
        if dataIn != '':
            self.url = f'http://{dataIn}:5000/'
            pass
        ui.label_3.setText(self.url)
        pass

    def getLocalIP(self):
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(('8.8.8.8', 80))
            ip = s.getsockname()[0]
        finally:
            s.close()
            pass
        return ip
        pass

    pass


if __name__ == '__main__':
    app = QApplication(sys.argv)
    board_ui = BoardGUI(LINES)
    board_ui.show()
    # board_ui.MainWindowUI.widget.hide()
    sys.exit(app.exec_())
    pass
